#include "stdafx.h"

#include <xnamath.h>

static XMMATRIX mmat = XMMatrixPerspectiveFovRH(1.57f,4.0f/3,0.01f,100.0f);


#define SOK(hr) if ((hr)!=S_OK) assert(false)

struct CBRenderConstants
{
	mat4f mProjection;
	mat4f mView;
	mat4f mModel;
};

class Dx11Render : public Render
{
public:
	Dx11Render(HWND hWnd)
	{
		mDevice = NULL;
		mSwapChain = NULL;
		mBackBufferView = NULL;
		mDepthView = NULL;
		mDeviceContext = NULL;
		mHwnd = hWnd;

		mMultiSample = 1;
		mSampleQuality = 0;
		mbWindowed = true;

		mClearColor[0] = 0.2f;
		mClearColor[1] = 0.2f;
		mClearColor[2] = 0.2f;
		mClearColor[3] = 1.0f;
	}
	~Dx11Render() 
	{
		deinitialize();
	}

	void initialize()
	{
		RECT rc;
		GetClientRect(mHwnd, &rc);
		size_t width = rc.right - rc.left;
		size_t height = rc.bottom - rc.top;

		size_t createDeviceFlags = 0;
#ifdef _DEBUG
		createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

		D3D_DRIVER_TYPE driverTypes[] =
		{
			D3D_DRIVER_TYPE_HARDWARE,
			D3D_DRIVER_TYPE_WARP,
		};
		size_t numDriverTypes = ARRAYSIZE(driverTypes);

		D3D_FEATURE_LEVEL featureLevels[] =
		{
			D3D_FEATURE_LEVEL_11_0,
			D3D_FEATURE_LEVEL_10_1,
			D3D_FEATURE_LEVEL_10_0,
			D3D_FEATURE_LEVEL_9_3,
			D3D_FEATURE_LEVEL_9_2,
			D3D_FEATURE_LEVEL_9_1,
		};
		size_t numFeatureLevels = ARRAYSIZE(featureLevels);

		DXGI_SWAP_CHAIN_DESC sd;
		ZeroMemory(&sd, sizeof(sd));
		sd.BufferCount = 1;
		sd.BufferDesc.Width = width;
		sd.BufferDesc.Height = height;
		sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
		sd.BufferDesc.RefreshRate.Numerator = 60;
		sd.BufferDesc.RefreshRate.Denominator = 1;
		sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
		sd.OutputWindow = mHwnd;
		sd.SampleDesc.Count = mMultiSample;
		sd.SampleDesc.Quality = mSampleQuality;
		sd.Windowed = mbWindowed;

		HRESULT hr = S_OK;
		for(UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++)
		{
			mDriverType = driverTypes[driverTypeIndex];
			hr = (D3D11CreateDeviceAndSwapChain(NULL, mDriverType, NULL, createDeviceFlags, featureLevels, numFeatureLevels,
				D3D11_SDK_VERSION, &sd, &mSwapChain, &mDevice, &mFeatureLevel, &mDeviceContext));
			if(SUCCEEDED(hr))
				break;
		}
		if(FAILED(hr)) cerror("failed to initialize device");

		// Create a render target for back buffer rendering
		ID3D11Texture2D* pBackBuffer = NULL;
		SOK(mSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( void** )&pBackBuffer ));
		SOK(mDevice->CreateRenderTargetView( pBackBuffer, NULL, &mBackBufferView ));
		pBackBuffer->Release();

		// Create depth stencil texture
		ID3D11Texture2D* depthStencil = NULL;
		D3D11_TEXTURE2D_DESC descDepth;
		memset(&descDepth, 0, sizeof(descDepth));
		descDepth.Width = width;
		descDepth.Height = height;
		descDepth.MipLevels = 1;
		descDepth.ArraySize = 1;
		descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
		descDepth.SampleDesc.Count = mMultiSample;
		descDepth.SampleDesc.Quality = mSampleQuality;
		descDepth.Usage = D3D11_USAGE_DEFAULT;
		descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL;
		descDepth.CPUAccessFlags = 0;
		descDepth.MiscFlags = 0;
		SOK(mDevice->CreateTexture2D(&descDepth, NULL, &depthStencil));

		// Create the depth stencil
		D3D11_DEPTH_STENCIL_VIEW_DESC descDSV;
		memset(&descDSV, 0, sizeof(descDSV));
		descDSV.Format = descDepth.Format;
		descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
		descDSV.Texture2D.MipSlice = 0;
		SOK(mDevice->CreateDepthStencilView(depthStencil, &descDSV, &mDepthView));
		depthStencil->Release();

		// depth state
		//D3D11_DEPTH_STENCIL_DESC depthStateDesc;
		//depthStateDesc.DepthEnable = true;
		//depthStateDesc.StencilEnable = true;
		//ID3D11DepthStencilState* depthState;
		//mDevice->CreateDepthStencilState(&depthStateDesc, &depthState);
		//mDeviceContext->OMSetDepthStencilState(depthState, 0);

		// set render target
		mDeviceContext->OMSetRenderTargets(1, &mBackBufferView, mDepthView);

		// rasterizer
		D3D11_RASTERIZER_DESC rasterDesc = {D3D11_FILL_SOLID, D3D11_CULL_NONE, FALSE, 0, 0.0f, 0.0f, TRUE, FALSE, FALSE, FALSE};
		mDevice->CreateRasterizerState(&rasterDesc, &mRasterizerSolid);
		mDeviceContext->RSSetState(mRasterizerSolid);
		rasterDesc.FillMode = D3D11_FILL_WIREFRAME;
		mDevice->CreateRasterizerState(&rasterDesc, &mRasterizerWireframe);

		// Setup the viewport
		D3D11_VIEWPORT vp;
		vp.Width = (FLOAT)width;
		vp.Height = (FLOAT)height;
		vp.MinDepth = 0.0f;
		vp.MaxDepth = 1.0f;
		vp.TopLeftX = 0;
		vp.TopLeftY = 0;
		mDeviceContext->RSSetViewports(1, &vp);

		mDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

		// sampler state
		D3D11_SAMPLER_DESC sampDesc;
		ZeroMemory(&sampDesc, sizeof(sampDesc));
		sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
		sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
		sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
		sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
		sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
		sampDesc.MinLOD = 0;
		sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
		mDevice->CreateSamplerState(&sampDesc, &mLinearSampler);
		mDeviceContext->PSSetSamplers(0, 1, &mLinearSampler);

		// blend state
		D3D11_BLEND_DESC blendState;
		ZeroMemory(&blendState, sizeof(D3D11_BLEND_DESC));
		blendState.IndependentBlendEnable = false;
		blendState.RenderTarget[0].BlendEnable = false;
		blendState.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
		SOK(mDevice->CreateBlendState( &blendState, &mBlendStateNoBlend ));
		blendState.RenderTarget[0].BlendEnable = true;
		blendState.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
		blendState.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
		blendState.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
		blendState.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
		blendState.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
		blendState.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
		blendState.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
		SOK(mDevice->CreateBlendState(&blendState, &mBlendState));
		mDeviceContext->OMSetBlendState(mBlendState, 0, 0xffffffff);
		//mDeviceContext->OMSetBlendState( mBlendStateNoBlend, 0, 0xffffffff );

		// initialize il
		ilInit();

		// initialize text
		//initFont();
		// matrix
		mRenderBuffer = createConstantBuffer(sizeof(CBRenderConstants), NULL);

		// default
	}

	void deinitialize() 
	{
		//mFontVerts.clear();

		deleteBuffer(TYPE_BUFFER,mRenderBuffer);
		//deleteBuffer(TYPE_SHADER_RESOURCE,mFont);
		//deleteBuffer(TYPE_BUFFER,fontCB);

		SAFE_RELEASE(mRasterizerSolid);
		SAFE_RELEASE(mRasterizerWireframe);
		SAFE_RELEASE(mLinearSampler);
		SAFE_RELEASE(mBlendState);
		SAFE_RELEASE(mBlendStateNoBlend);
		SAFE_RELEASE(mBackBufferView);
		SAFE_RELEASE(mDepthView);
		SAFE_RELEASE(mSwapChain);

		if(mDeviceContext)
		{
			mDeviceContext->ClearState();
			mDeviceContext->Flush();
		}
		SAFE_RELEASE(mDeviceContext);
		SAFE_RELEASE(mDevice);
	}

	void clear()
	{
		mDeviceContext->ClearRenderTargetView(mBackBufferView,mClearColor);
		mDeviceContext->ClearDepthStencilView(mDepthView, D3D11_CLEAR_DEPTH, 1.0f, 0);

		// Set the constant buffers
		setConstantBuffer(mRenderBuffer, Render::TYPE_VERTEX_SHADER);
		//setConstantBuffer(mRenderBuffer, Render::TYPE_GEOMETRY_SHADER);
		//setConstantBuffer(mRenderBuffer, Render::TYPE_PIXEL_SHADER);
	}

	void flush() const
	{
		mSwapChain->Present(0,0);
	}

	void syncronize()
	{
		mDeviceContext->Flush();
	}

	void initFont()
	{
		mFont = loadTexture("couriernew24.png");
		//fontCB = createConstantBuffer(sizeof(CBMatrix),NULL);
	}

	//void beginText()
	//{
	//	mFontVerts.clear();

	//	XMMATRIX proj = XMMatrixOrthographicLH((float)mWinWidth,(float)mWinHeight,0.1f,1.0f);
	//	XMStoreFloat4x4(&mRenderConst.mProjection,proj);
	//}

	//void endText()
	//{
	//	pint fontLayout, fontBuffer;
	//	VertexDescriptor vd;
	//	vd.hasTexCoords[0] = true;
	//	vd.texCoordStride[0] = 2;
	//	vd.texCoordOffset[0] = sizeof(float)*3;
	//	createVertexBuffer(&(mFontVerts.front()),mFontVerts.size()*sizeof(mFontVerts[0]),sizeof(mFontVerts[0]),0,defaultVBlob,fontBuffer,fontLayout,&vd);
	//	// draw
	//	applyShader(defaultVShader,defaultPShader,pint(0));
	//	applyTexture(0,mFont);
	//	drawBuffer(mFontVerts.size(),0,RENDER_TYPE_TRILIST);
	//	// clear
	//	deleteBuffer(TYPE_BUFFER,fontBuffer);
	//	deleteBuffer(TYPE_LAYOUT,fontLayout);
	//}


	//void drawText(const std::string& str, int x, int y, double scaleX,double scaleY)
	//{
	//	float FONT_TEX_X = 1 / 16.0f;
	//	float FONT_TEX_Y = 1 / 16.0f;
	//	// get the length of text
	//	size_t strLen = str.length();
	//	int currLine = 0;
	//	int currCol = 0;

	//	for (size_t i=0;i<strLen;++i)
	//	{
	//		// get ascii
	//		char c = str[i];
	//		if (c=='\n') { currLine++; currCol = 0; continue; }
	//		int row = c / 16;
	//		int col = c % 16;
	//		// calculate texcood
	//		float txl = col * FONT_TEX_X;
	//		float txr = txl + FONT_TEX_X;
	//		float tyd = row * FONT_TEX_Y;
	//		float tyu = tyd + FONT_TEX_Y;

	//		// calculate position
	//		float xpl = (float)x + currCol*FONT_WIDTH - mWinWidth/2;
	//		float xpr = xpl + FONT_WIDTH;
	//		float ypu = mWinHeight/2 - FONT_HEIGHT - float(y) - currLine*FONT_HEIGHT;
	//		float ypd = ypu + FONT_HEIGHT;

	//		// create vertex buffer
	//		vec3f vt1;
	//		vt1.x = xpl; vt1.y = ypu; vt1.z = 0; vt1.u = txl; vt1.v = tyu;
	//		mFontVerts.push_back(vt1);
	//		vec3f vt2;
	//		vt2.x = xpr; vt2.y = ypu; vt2.z = 0; vt2.u = txr; vt2.v = tyu;
	//		mFontVerts.push_back(vt2);
	//		vec3f vt3;
	//		vt3.x = xpl; vt3.y = ypd; vt3.z = 0; vt3.u = txl; vt3.v = tyd;
	//		mFontVerts.push_back(vt3);
	//		mFontVerts.push_back(vt2);
	//		mFontVerts.push_back(vt3);
	//		vec3f vt6;
	//		vt6.x = xpr; vt6.y = ypd; vt6.z = 0; vt6.u = txr; vt6.v = tyd;
	//		mFontVerts.push_back(vt6);

	//		currCol++;
	//	}
	//}

	pint loadShader(const std::string& fileName, int type, const std::string& func, pint* inputLayout, const VertexDesc* desc) const
	{
		pint out;

		DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#ifdef _DEBUG
		dwShaderFlags |= D3DCOMPILE_DEBUG;
#endif
		std::wstring wfileName;
		string2wstring(SHADER_PATH + fileName,wfileName);

		ID3DBlob* pBlob = NULL;
		ID3DBlob* errblob = NULL;

		if (type == Shader_Vertex)
		{
			const char* pProfile = (mDevice->GetFeatureLevel() >= D3D_FEATURE_LEVEL_11_0) ? "vs_5_0" : "vs_4_0";
			if( FAILED( D3DX11CompileFromFile(wfileName.c_str(), NULL, NULL, func.c_str(), pProfile, 
				dwShaderFlags, 0, NULL, &pBlob, &errblob, NULL)))
			{
				CompileErrorHandle(errblob);
			}

			ID3D11VertexShader* vertexShader = NULL;
			
			// Create the vertex shader
			SOK(mDevice->CreateVertexShader(pBlob->GetBufferPointer(), 
				pBlob->GetBufferSize(), NULL, &vertexShader));

			assert(vertexShader);

			// create input layout
			if (desc)
			{
				// Define the input layout based on vertex descriptor
				std::vector<D3D11_INPUT_ELEMENT_DESC> layout;
				D3D11_INPUT_ELEMENT_DESC pos =
				{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, desc->positionOffset, D3D11_INPUT_PER_VERTEX_DATA, 0 };
				layout.push_back(pos);
				//if (desc->bHasNormal)
				//{
				//	D3D11_INPUT_ELEMENT_DESC tmp = 
				//	{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, desc->normalOffset, D3D11_INPUT_PER_VERTEX_DATA, 0 };
				//	layout.push_back(tmp);
				//}
				//if (desc->bHasColor)
				//{
				//	D3D11_INPUT_ELEMENT_DESC tmp = 
				//	{ "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, desc->colorOffset, D3D11_INPUT_PER_VERTEX_DATA, 0 };
				//	layout.push_back(tmp);
				//}
				//for (int i=0;i<MAX_TEXCOORD_INDEX;++i)
				//{
				//	if (desc->bHasTexcoord[i])
				//	{
				//		D3D11_INPUT_ELEMENT_DESC tmp = 
				//		{ "TEXCOORD", i, DXGI_FORMAT_R32G32B32_FLOAT, 0, desc->texcoordOffset[i], D3D11_INPUT_PER_VERTEX_DATA, 0 };
				//		layout.push_back(tmp);
				//	}
				//}
				UINT numElements = layout.size();

				// Create the input layout
				ID3D11InputLayout* pInputLayout = NULL;

				// Create the input layout
				assert(pBlob);
				SOK(mDevice->CreateInputLayout( &(layout.front()), numElements, pBlob->GetBufferPointer(),
					pBlob->GetBufferSize(), &pInputLayout ));
				
				*inputLayout = pInputLayout;
			}
			pBlob->Release();
			out = vertexShader;
		}
		else if (type == Shader_Pixel)
		{
			const char* pProfile = (mDevice->GetFeatureLevel() >= D3D_FEATURE_LEVEL_11_0) ? "ps_5_0" : "ps_4_0";
			if( FAILED( D3DX11CompileFromFile(wfileName.c_str(), NULL, NULL, func.c_str(), pProfile, 
				dwShaderFlags, 0, NULL, &pBlob, &errblob, NULL)))
			{
				CompileErrorHandle(errblob);
			}

			ID3D11PixelShader* pixelShader = NULL;
			mDevice->CreatePixelShader(pBlob->GetBufferPointer(), pBlob->GetBufferSize(), NULL, &pixelShader);
			pBlob->Release();

			assert(pixelShader);
			out = pixelShader;
		}
		else if (type == Shader_Geometry)
		{
			const char* pProfile = (mDevice->GetFeatureLevel() >= D3D_FEATURE_LEVEL_11_0) ? "gs_5_0" : "gs_4_0";
			if( FAILED( D3DX11CompileFromFile(wfileName.c_str(), NULL, NULL, func.c_str(), pProfile, 
				dwShaderFlags, 0, NULL, &pBlob, &errblob, NULL)))
			{
				CompileErrorHandle(errblob);
			}

			ID3D11GeometryShader* geometryShader = NULL;
			mDevice->CreateGeometryShader(pBlob->GetBufferPointer(), pBlob->GetBufferSize(), NULL, &geometryShader);
			pBlob->Release();

			assert(geometryShader);
			out = geometryShader;
		}
		else if (type == Shader_Compute)
		{
			const char* pProfile = (mDevice->GetFeatureLevel() >= D3D_FEATURE_LEVEL_11_0) ? "cs_5_0" : "cs_4_0";
			if( FAILED( D3DX11CompileFromFile(wfileName.c_str(), NULL, NULL, func.c_str(), pProfile, 
				dwShaderFlags, 0, NULL, &pBlob, &errblob, NULL)))
			{
				CompileErrorHandle(errblob);
			}

			const D3D_SHADER_MACRO defines[] = 
			{
				NULL, NULL
			};

			ID3D11ComputeShader* computeShader = NULL;
			mDevice->CreateComputeShader(pBlob->GetBufferPointer(), pBlob->GetBufferSize(), NULL, &computeShader);
			pBlob->Release();

			assert(computeShader);
			out = computeShader;
		}

		SAFE_RELEASE(errblob);

		return out;
	}

	pint loadTexture(const std::string& fileName) const
	{
		pint out;

		ILuint idImage;
		ilGenImages(1,&idImage);
		ilBindImage(idImage);

		std::wstring wFileName;
		string2wstring(fileName, wFileName);
		ilLoadImage(wFileName.c_str());

		ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);

		ILint width = ilGetInteger(IL_IMAGE_WIDTH);
		ILint height = ilGetInteger(IL_IMAGE_HEIGHT);

		std::auto_ptr<unsigned char>pBuffer(new unsigned char[width * height * 4]);
		ilCopyPixels(0, 0, 0, width, height, 1, IL_RGBA, IL_UNSIGNED_BYTE, pBuffer.get());

		ilDeleteImages(1, &idImage);
		idImage = 0;

		if (ilGetError() != IL_NO_ERROR)
		{
			cwarn("texture load failed, check if file exist.\n");
			out = 0;
			return out;
		}

		// allocate a texture name
		// create texture
		// based on the size of image, determine the miplevel
		size_t miplevel = logbase<int>((width<height ? width : height), 2);
		miplevel = miplevel > 7 ? 7 : miplevel;
		ID3D11Texture2D* tex;
		D3D11_TEXTURE2D_DESC td = {
			width, height, miplevel, 1, DXGI_FORMAT_R8G8B8A8_UNORM,
			{mMultiSample, mSampleQuality}, D3D11_USAGE_DEFAULT, 
			D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET, 0, D3D11_RESOURCE_MISC_GENERATE_MIPS
		};
		D3D11_SUBRESOURCE_DATA* resData = new D3D11_SUBRESOURCE_DATA[miplevel];
		for (int i = 0; i < (int)miplevel; ++i)
		{
			resData[i].pSysMem = pBuffer.get();
			resData[i].SysMemPitch = 4 * width / (int)(pow(2.0f,i));
		}

		SOK(mDevice->CreateTexture2D(&td, resData, &tex));

		// Create the shader resource view
		D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
		srvDesc.Format = td.Format;
		srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
		srvDesc.Texture2D.MostDetailedMip = 0;
		srvDesc.Texture2D.MipLevels = miplevel;

		ID3D11ShaderResourceView* psrv;
		SOK(mDevice->CreateShaderResourceView(tex, &srvDesc, &psrv));

		mDeviceContext->GenerateMips(psrv);

		out = psrv;
		assert(out);

		delete[] resData;
		tex->Release();

		return out;
	}

	void loadMatrix(RenderEnum type, const mat4f& matrix)
	{
		// translate from row major to column major
		mat4f& cMat = matrix.transpose();

		switch(type)
		{
		case MODEL_MATRIX:
			memcpy_s(&mRenderConst.mModel, mat4f::MATRIX44_SIZE, cMat.m, mat4f::MATRIX44_SIZE);
			break;
		case VIEW_MATRIX:
			memcpy_s(&mRenderConst.mView, mat4f::MATRIX44_SIZE, cMat.m, mat4f::MATRIX44_SIZE);
			break;
		case PROJ_MATRIX:
			memcpy_s(&mRenderConst.mProjection, mat4f::MATRIX44_SIZE, cMat.m, mat4f::MATRIX44_SIZE);
			break;
		case TEXTURE_MATRIX:
			break;
		default:
			break;
		}
	}

	void applyShader(pint shader, int type) const
	{
		switch(type)
		{
		case Shader_Vertex:
			mDeviceContext->VSSetShader(reinterpret_cast<ID3D11VertexShader*>(shader), NULL, 0);
			break;
		case Shader_Pixel:
			mDeviceContext->PSSetShader(reinterpret_cast<ID3D11PixelShader*>(shader), NULL, 0);
			break;
		case Shader_Geometry:
			mDeviceContext->GSSetShader(reinterpret_cast<ID3D11GeometryShader*>(shader), NULL, 0);
			break;
		}
	}

	pint createConstantBuffer(size_t size,const void* data)
	{
		D3D11_BUFFER_DESC desc;
		desc.Usage = D3D11_USAGE_DEFAULT;
		desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
		desc.CPUAccessFlags = 0;
		desc.MiscFlags = 0;
		desc.ByteWidth = size;

		ID3D11Buffer* pbuffer = NULL;

		if (data)
		{
			D3D11_SUBRESOURCE_DATA InitData;
			InitData.pSysMem = data;
			SOK(mDevice->CreateBuffer( &desc, &InitData, &pbuffer ));
		}
		else
		{
			SOK(mDevice->CreateBuffer( &desc, NULL, &pbuffer ));
		}

		pint out;
		out = pbuffer;
		return out;
	}

	void setConstantBuffer(pint buffer, RenderEnum type)
	{
		ID3D11Buffer* pBuffer = reinterpret_cast<ID3D11Buffer*>(buffer);
		switch (type)
		{
		case TYPE_VERTEX_SHADER:
			mDeviceContext->VSSetConstantBuffers( 0, 1, &pBuffer );
			break;
		case TYPE_GEOMETRY_SHADER:
			mDeviceContext->GSSetConstantBuffers( 0, 1, &pBuffer );
			break;
		case TYPE_PIXEL_SHADER:
			mDeviceContext->PSSetConstantBuffers( 0, 1, &pBuffer );
			break;
		case TYPE_NONE:
		default:
			mDeviceContext->CSSetConstantBuffers( 0, 1, &pBuffer );
			break;
		}
	}

	void setVertexBuffer(const pint buffer, size_t sizeOfVertex, size_t offset) const
	{
		// Set vertex buffer
		mDeviceContext->IASetVertexBuffers(0, 1, (reinterpret_cast<ID3D11Buffer*const*>(&buffer)), &sizeOfVertex, &offset);
	}
	void setVertexLayout(const pint layout) const
	{
		// Set the input layout
		mDeviceContext->IASetInputLayout(reinterpret_cast<ID3D11InputLayout*>(layout));
	}
	void setIndexBuffer(pint buffer, size_t offset) const
	{
		mDeviceContext->IASetIndexBuffer(reinterpret_cast<ID3D11Buffer*>(buffer), DXGI_FORMAT_R32_UINT, offset);
	}

	pint createVertexBuffer(const void* data, size_t bufferSize) const
	{
		ID3D11Buffer* pVBuffer = NULL;

		D3D11_BUFFER_DESC bd;
		bd.Usage = D3D11_USAGE_DEFAULT;
		bd.ByteWidth = bufferSize;
		bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
		bd.CPUAccessFlags = 0;
		bd.MiscFlags = 0;
		D3D11_SUBRESOURCE_DATA InitData;
		InitData.pSysMem = data;
		SOK( mDevice->CreateBuffer( &bd, &InitData, &pVBuffer ) );

		return pVBuffer;
	}
	
	pint createIndexBuffer(const void* data, size_t size)
	{
		D3D11_SUBRESOURCE_DATA srd = { data, 0, 0 };
		CD3D11_BUFFER_DESC bd;
		bd.ByteWidth = size;
		bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
		bd.Usage = D3D11_USAGE_IMMUTABLE;
		bd.CPUAccessFlags = 0;
		bd.MiscFlags = 0;
		ID3D11Buffer* pBuffer = NULL;
		SOK(mDevice->CreateBuffer(&bd, &srd, &pBuffer));

		return pBuffer;
	}

	void updateResource(pint res, const void* data) const
	{
		ID3D11Buffer* pBuffer = reinterpret_cast<ID3D11Buffer*>(res);
		mDeviceContext->UpdateSubresource( pBuffer, 0, NULL, data, 0, 0 );
	}

	void drawIndexBuffer(size_t numIndicies, size_t startIndex = 0, size_t startVertex = 0) const
	{
		updateResource(mRenderBuffer, &mRenderConst);
		// Render
		mDeviceContext->DrawIndexed(numIndicies, startIndex, startVertex);
	}

	void deleteShader(int type,pint bufferId) const
	{
		if (bufferId == NULL) return;
		switch (type)
		{
		case Shader_Vertex:
			SOK(reinterpret_cast<ID3D11VertexShader*>(bufferId)->Release());
			break;
		case Shader_Pixel:
			SOK(reinterpret_cast<ID3D11PixelShader*>(bufferId)->Release());
			break;
		case Shader_Geometry:
			SOK(reinterpret_cast<ID3D11GeometryShader*>(bufferId)->Release());
			break;
		case Shader_Compute:
			SOK(reinterpret_cast<ID3D11ComputeShader*>(bufferId)->Release());
			break;
		default:
			break;
		}
	}

	void deleteBuffer(RenderEnum type,pint bufferId) const
	{
		if (bufferId == NULL) return;
		switch (type)
		{
		case TYPE_LAYOUT:
			SOK(reinterpret_cast<ID3D11InputLayout*>(bufferId)->Release());
			break;
		case TYPE_SHADER_RESOURCE:
			SOK(reinterpret_cast<ID3D11ShaderResourceView*>(bufferId)->Release());
			break;
		case TYPE_UNORDERED_ACCESS_VIEW:
			SOK(reinterpret_cast<ID3D11UnorderedAccessView*>(bufferId)->Release());
			break;
		default:
			SOK(reinterpret_cast<ID3D11Buffer*>(bufferId)->Release());
			break;
		}
	}
private:
	void CompileErrorHandle(ID3DBlob* errblob) const
	{
		if (!errblob) return;
		char* err = (char*)errblob->GetBufferPointer();
		size_t size = errblob->GetBufferSize();
		wchar_t* werr = new wchar_t[size*2];
		MultiByteToWideChar(CP_ACP, NULL, err, size, werr, size);
		OutputDebugString(werr);
		cerror("compile shader failed." << werr);
		errblob->Release();
		delete[] werr;
	}

public:// variable
	HWND mHwnd;

	D3D_DRIVER_TYPE mDriverType;
	D3D_FEATURE_LEVEL mFeatureLevel;

	ID3D11Device* mDevice;
	IDXGISwapChain* mSwapChain;
	ID3D11DeviceContext* mDeviceContext;
	ID3D11RasterizerState* mRasterizerWireframe;
	ID3D11RasterizerState* mRasterizerSolid;

	pint defaultVShader,defaultPShader,defaultVBlob;
	pint mFont;
	pint fontCB;
	//std::vector<FontVert> mFontVerts;

	pint mRenderBuffer;
	CBRenderConstants mRenderConst;

	ID3D11RenderTargetView* mBackBufferView;
	ID3D11DepthStencilView* mDepthView;
	ID3D11SamplerState* mLinearSampler;
	ID3D11BlendState* mBlendState;
	ID3D11BlendState* mBlendStateNoBlend;
};

Render* createDx11Render( HWND hWnd )
{
	Render* render = new Dx11Render(hWnd);
	render->initialize();

	return render;
}